using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace XXY
{
    class XMLUtil
    {
        #region 创建并初始化
        public XMLUtil(string xmlStr)
        {
            this.xmlStr = xmlStr;
            Reduce();
            Init();
        }
        private void Reduce() //xml 压缩
        {
            // 一下匹配需要注意先后顺序

            // /\*[\s\S]*?\*/
            // 移除多行注释
            // <[^<>]*?/>
            // 移除没用标签 移除 <book  /> 和 <book a="fdsa" /> 类型的标签
            // (?<=>)\s*(?=<)
            // 匹配标签中间的空白字符
            // ^\s*
            // 匹配开头空白字符

            string[] patternStrs = new string[] { "/\\*[\\s\\S]*?\\*/", "<[^<>]*?/>", "(?<=>)\\s*(?=<)", "^\\s*" };
            foreach (string str in patternStrs)
            {
                xmlStr = Regex.Replace(xmlStr, str, "");
            }
        }
        private void Init()
        {
            #region 初始化 头和身体 编码 版本
            // ^<\?xml[\w\W]+?\?>
            // 匹配 <?xml version="1.0" encoding="UTF-8"?>
            string patternStr = "^<\\?xml[\\w\\W]+?\\?>";
            Match match = Regex.Match(xmlStr, patternStr);
            if (match.Success)
            {
                headStr = match.Value;
                bodyStr = xmlStr.Substring(match.Index + match.Length);
                version = getAttrValue(headStr, "version");
                encoding = getAttrValue(headStr, "encoding");
            }
            else
            {
                bodyStr = xmlStr;
            }
            #endregion
            #region 解析xml 解析身体
            xmlObj = new XMLElement();
            xmlObj.parent = null;
            xmlObj.bodyStr = getBodyStr(bodyStr);
            xmlObj.headStr = getHeadStr(bodyStr);
            xmlObj.tagStr = getTagStr(xmlObj.headStr);
            xmlObj.attibutes = new Hashtable();
            setAtttibutes(xmlObj.headStr, xmlObj.attibutes);
            // TODO 解析 子类集合
            parsingBody(xmlObj);
            #endregion
        }
        #endregion
        #region Util
        public void print(string msg)
        {
            Console.WriteLine(msg);
        }
        /// <summary>
        /// XMLElement 对象转化成 string
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string XMLElementToString(XMLElement xml)
        {
            string str = "";
            if (xml != null)
            {
                //region Head string
                str += "<" + xml.tagStr;
                if (xml.attibutes.Count != 0)
                {
                    IDictionaryEnumerator enumerator = xml.attibutes.GetEnumerator();
                    while (enumerator.MoveNext())
                    {
                        if(enumerator.Value.ToString().Contains("\""))
                        {
                            str += " " + enumerator.Key + "='" + enumerator.Value + "'";
                        }
                        else
                        {
                            str += " " + enumerator.Key + "=\"" + enumerator.Value + "\"";
                        } 
                    }
                }
                str += ">";
                //endregion
                //region Body string
                if (xml.childs.Count == 0)
                {
                    str += xml.bodyStr;
                }
                else
                {
                    for (int i = 0; i < xml.childs.Count; i++)
                    {
                        str += XMLElementToString(xml.childs[i]);
                    }
                }
                //endregion
                str += "</" + xml.tagStr + ">";
            }
            return str;
        }
        #endregion
        #region 属性
        private string xmlStr;      // 要解析的xml字符串
        public string version = "";
        public string encoding = "";
        public XMLElement xmlObj = null;
        private string headStr = "";// <?xml version="1.0" encoding="UTF-8"?>
        private string bodyStr = "";// xml基本组成部分
        #endregion
        #region 内部解析(parsing)实现 
        /// <summary>
        /// 获取属性值
        /// </summary>
        /// <param name="matchHeadStr">要匹配的字符串,尖括号内容，根节点内容（headStr）</param>
        /// <param name="keyStr">要匹配值的键</param>
        /// <returns></returns>
        private string getAttrValue(string matchHeadStr, string keyStr)
        {
            // (?<=version\s*=\s*")[\s\S]+?(?=")
            // (?<=version\s*=\s*')[\s\S]+?(?=')
            // 匹配 version="1.0.56" version='1.0.56'   的 1.0.56

            string[] patternStr = new string[] { "(?<=" + keyStr + "\\s{0,100}=\\s{0,100}\")[\\s\\S]+?(?=\")", "(?<=" + keyStr + "\\s{0,100}=\\s{0,100}')[\\s\\S]+?(?=')" };
            foreach (string pattern in patternStr)
            {
                Match match = Regex.Match(matchHeadStr, pattern);
                if (match.Success)
                {
                    return match.Value;
                }
            }
            return "";

        }

        /// <summary>
        /// 获取根节点标签
        /// </summary>
        /// <param name="matchHeadStr">要匹配的字符串,尖括号内容  根节点内容（headStr）</param>
        /// <returns></returns>
        private string getTagStr(string matchHeadStr)
        {
            // (?<=<)[^/><。?？."'‘’“”\\\s]+(?=(\s|>))
            // 匹配 根节点标签

            string patternStr = "(?<=<)[^/><。?？.\"'‘’“”\\\\\\s]+(?=(\\s|>))";
            Match match = Regex.Match(matchHeadStr, patternStr);
            if (match.Success)
            {
                return match.Value;
            }
            print("没有匹配到标签，出现异常");
            return "";
        }

        /// <summary>
        /// 获取根节点 <tree age="100" name='白杨树'> 尖括号内容
        /// </summary>
        /// <param name="matchXmlStr">要匹配的字符串,尖括号内容</param>
        /// <returns></returns>
        private string getHeadStr(string matchXmlStr)
        {
            // ^<[\w\W]+?>
            // 字符串开头 根节点头部

            string patternStr = "^<[\\w\\W]+?>";
            Match match = Regex.Match(matchXmlStr, patternStr);
            if (match.Success)
            {
                return match.Value;
            }
            return "";
        }

        /// <summary>
        /// 获取根节点下的所有节点，或值
        /// </summary>
        /// <param name="matchXmlStr">要匹配的字符串,尖括号内容</param>
        /// <returns></returns>
        private string getBodyStr(string matchXmlStr)
        {
            // (?<=>)[\w\W]+(?=</)
            // 匹配根节点下的所有子节点，或者值

            string patternStr = "(?<=>)[\\w\\W]+(?=</)";
            Match match = Regex.Match(matchXmlStr, patternStr);
            if (match.Success)
            {
                return match.Value;
            }
            return "";
        }

        /// <summary>
        /// 匹配所有根节点属性键
        /// </summary>
        /// <param name="matchHeadStr">要匹配的字符串,尖括号内容  根节点内容（headStr）</param>
        /// <returns></returns>
        private List<string> getAttibutesKey(string matchHeadStr)
        {
            // (?<=\s)[^/><。?？."'‘’“”\\\s]+?(?=\s|=)
            // 匹配所有根节点内的属性键名

            List<string> strs = new List<string>();
            string patternStr = "(?<=\\s)[^/><。?？.\"'‘’“”\\\\\\s]+?(?=\\s|=)";
            MatchCollection matchs = Regex.Matches(matchHeadStr, patternStr);
            foreach (Match match in matchs)
            {
                strs.Add(match.Value);
            }
            return strs;
        }

        /// <summary>
        /// 设置对象属性集合
        /// </summary>
        /// <param name="matchHeadStr">要匹配的字符串,尖括号内容  根节点内容（headStr）</param>
        /// <param name="attibutes">要赋值的集合</param>
        private void setAtttibutes(string matchHeadStr, Hashtable attibutes)
        {
            List<string> keys = getAttibutesKey(matchHeadStr);
            for (int i = 0; i < keys.Count; i++)
            {
                string value = getAttrValue(matchHeadStr, keys[i]);
                if (value != "" && keys[i] != "" && !attibutes.ContainsKey(keys[i]))
                {
                    attibutes.Add(keys[i], value);
                }
            }
        }

        /// <summary>
        /// 获取bodyStr子节点
        /// </summary>
        /// <param name="matchBodyStr">所有的子类</param>
        /// <returns></returns>
        private List<string> getChildStrs(string matchBodyStr)
        {
            List<string> Strs = new List<string>();
            string tag = "";
            // ^<[\w]+(?=(\s|>))
            // 匹配标签
            string patternStr = "^<[\\w]+(?=(\\s|>))";
            Match matchs = Regex.Match(matchBodyStr, patternStr);
            while (matchs.Success)
            {
                tag = matchs.Value.Substring(1);
                // </book>
                // 匹配结束标签
                string s = "</" + tag + ">";
                MatchCollection mc = Regex.Matches(matchBodyStr, s);
                foreach (Match m in mc)
                {
                    if (m.Success && getLayer(matchBodyStr.Substring(0, m.Index + m.Length), tag) == 0)
                    {
                        Strs.Add(matchBodyStr.Substring(0, m.Index + m.Length));
                        matchBodyStr = matchBodyStr.Substring(m.Index + m.Length);
                        break;
                    }
                }
                matchs = Regex.Match(matchBodyStr, patternStr);
            }
            return Strs;
        }
        /// <summary>
        /// 返回标签所在层级
        /// </summary>
        /// <param name="xml">要匹配的字符串</param>
        /// <param name="tag">要匹配的标签</param>
        /// <returns>返回tag在xml所在层级 0: 表示开始标签和结束标签在同一层级</returns>
        private int getLayer(string xml, string tag)
        {
            // <book(?=\s|>)
            // 匹配开始标签
            // </book>
            // 匹配结束标签

            int headTag = 0;
            int endTag = 0;
            string patternStr = "<" + tag + "(?=\\s|>)";
            MatchCollection matchs = Regex.Matches(xml, patternStr);
            headTag = matchs.Count;
            patternStr = "</" + tag + ">";
            matchs = Regex.Matches(xml, patternStr);
            endTag = matchs.Count;
            return headTag - endTag;
        }
        /// <summary>
        /// 解析节点获取所有子类
        /// </summary>
        /// <param name="xmlElement"></param>
        private void parsingBody(XMLElement xmlElement)
        {
            List<string> Strs = getChildStrs(xmlElement.bodyStr);
            xmlElement.childs = new List<XMLElement>();
            for (int i = 0; i < Strs.Count; i++)
            {
                XMLElement child = new XMLElement();
                child.parent = xmlElement;
                child.bodyStr = getBodyStr(Strs[i]);
                child.headStr = getHeadStr(Strs[i]);
                child.tagStr = getTagStr(child.headStr);
                child.attibutes = new Hashtable();
                setAtttibutes(child.headStr, child.attibutes);
                parsingBody(child);
                xmlElement.childs.Add(child);
            }
        }

        #endregion
        #region 公开方法
        /// <summary>
        /// 将 xmlObj 解析成xml  字符串
        /// </summary>
        /// <returns></returns>
        public string toString()
        {

            string xml = "";
            //region XML 编码 版本
            if (version != "")
            {
                xml += "<?xml version=\"" + version + "\"?>";
            }
            if (encoding != "")
            {
                if (xml.Length <= 1)
                {
                    xml += "<?xml encoding=\"" + encoding + "\"?>";
                }
                else
                {
                    xml = xml.Replace("?>", " encoding=\"" + encoding + "\"?>");
                }
            }
            //endregion
            //region xml 内容
            xml += XMLElementToString(xmlObj);
            //endregion
            return xml;
        }

        #endregion

        public class XMLElement
        {
            #region 属性
            public XMLElement parent;
            public List<XMLElement> childs;
            public Hashtable attibutes;
            public string bodyStr;                     // 值  所有的子类，或值
            public string tagStr;                     // 标签
            public string headStr;                   // 尖括号内容   <book category="CHILDREN">
            #endregion
            #region 方法
            /// <summary>
            /// 查找节点树，通过标签
            /// </summary>
            /// <param name="tag">子节点标签 </param>
            /// <returns>获取所有子类标签为tag的对象</returns>
            public List<XMLElement> getXMLElementsByTag(string tag)
            {
                List<XMLElement> child = new List<XMLElement>();
                if (childs != null)
                {
                    for (int i = 0; i < childs.Count; i++)
                    {
                        if (childs[i].tagStr == tag)
                        {
                            child.Add(childs[i]);
                        }
                    }
                }
                return child;
            }
            /// <summary>
            /// 查找节点树，通过标签
            /// </summary>
            /// <param name="tag">子节点标签 </param>
            /// <returns>返回第一次匹配到的标签为tag的对象</returns>
            public XMLElement getXMLElementByTag(string tag)
            {
                if (childs != null)
                {
                    for (int i = 0; i < childs.Count; i++)
                    {
                        if (childs[i].tagStr == tag)
                        {
                            return childs[i];
                        }
                    }
                }
                return null;
            }
            #endregion
        }
    }
}
